package com.google.code

import scala.annotation.tailrec

object UserControl {
  private val addChildNoAttsp = "^addchild (.+?)".r
  private val addChildAttsp = "^addchild (.+?) (.+?)".r
  private val singleAttributep ="\\s*(.+?)=\\\"(.+?)\\\"\\s*".r

  private val removeNodep = "^remove$".r

  private val quitp = "^quit$".r
  private val savep = "^save$".r

  def run(document: XmlDocument): Unit = {
    println()
    println(document.toString)

    val (doc, quit) = parseAndExecute(document)

    if(!quit) run(doc)
  }

  @tailrec
  private def nodeParentList(node: XmlNode, res: List[Node]): List[Node] = node.parent match {
    case None => res
    case Some(n) =>
      nodeParentList(n, n :: res)
  }

  def parseAndExecute(document: XmlDocument): (XmlDocument, Boolean) = document.entry match {
    case Some(entry: Node) =>
      print("select> ")

      val rl = readLine()
      val selectCommand =
        if(rl startsWith "/")
          rl.substring(1) split "/"
        else
          rl split "/"

      val (startNode: Option[XmlNode], newSelectCommand) =
        if(selectCommand.length > 0 && (selectCommand.head equals entry.nodeName))
          (Some[XmlNode](entry), selectCommand.tail)
        else {
          print("command> ")
          readLine() match {
            case savep() =>
              document.save()
              return (document, false)

            case quitp() =>
              return (document, true)

            case _ =>
              println("No such command")
              return (document, false)
          }
        }

      val selectedNode = newSelectCommand.foldLeft(startNode){
        case (r, name) =>
          r flatMap (_ \ name)
      }

      print("command> ")
      val command = readLine()

      val addChild = { newNode: Option[Node] =>
        val nodeParents = newNode map {e =>
          val a = nodeParentList(e, Nil)
          a.foldLeft(e){
            case (n, p) =>
              println(n, " -- ", p)
              p.changeChild(n)
          }
        }

        nodeParents
      }

      command match {
        case addChildAttsp(name, attributes) =>
          val atts = singleAttributep.findAllIn(attributes).matchData.map(m => Attribute(m.group(1), m.group(2))).toList

          val newNode = selectedNode flatMap {
            case n: Node => Some(n.addChild(Node(name, atts, Some(n), Nil)))
            case c: Comment => None
          }

          (document.changeEntry(addChild(newNode)), false)

        case addChildNoAttsp(name) =>
          val newNode = selectedNode flatMap {
            case n: Node => Some(n.addChild(Node(name, Nil, Some(n), Nil)))
            case c: Comment => None
          }

          (document.changeEntry(addChild(newNode)), false)

        case removeNodep() =>
          val nodeParents = selectedNode flatMap { e =>
            val (a,b) = nodeParentList(e, Nil) match {
              case Nil => (None,Nil)
              case x :: xs => val t = x.removeChild(e)
                (Some(t), t :: xs)
            }
            a map ( aa => b.foldLeft(aa){
              case (n, p) =>
                p.changeChild(n)
            })
          }

          (document.changeEntry(nodeParents), false)

        case savep() =>
          document.save()
          (document, false)

        case quitp() =>
          (document, true)

        case _ =>
          println("No such command")
          (document, false)

    }

    case None =>
      print("command> ")
      val command = readLine()

      command match {
        case addChildAttsp(name, attributes) =>
          val atts = singleAttributep.findAllIn(attributes).matchData.map(m => Attribute(m.group(1), m.group(2))).toList

          val newNode = Node(name, atts, None, Nil)
          (document.changeEntry(Some(newNode)), false)

        case addChildNoAttsp(name) =>
          val newNode = Node(name, Nil, None, Nil)
          (document.changeEntry(Some(newNode)), false)

        case savep() =>
          document.save()
          (document, false)

        case quitp() =>
          (document, true)

        case _ =>
          println("No such command")
          (document, false)

      }

    case _ =>
      println("Something went terribly wrong")
      (document, true)
  }

}
